﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Configuration;
using System.IO;
using Dominio.Entidades;
using System.Xml;
using Dominio.Excepciones;
using InterfazPublica;
using Dominio.ObserverEntidades;

namespace Dominio.Logica
{
    public class Agencia : EntidadesSubject
    {
        private static Agencia agencia;

        public Usuarios UsuarioLogueado { get; set; }

        public List<Juegos> ListaJuegos { get; set; }

        public List<Assembly> ListaAssemblies { get; set; }

        public List<IJuegos> ListaEntidades { get; set; }

        public ManejadorApuestas ManejadorApuestas { get; set; }

        private Agencia() { }

        public static Agencia GetAgencia()
        {
            if (agencia == null)
            {
                agencia = new Agencia();
                agencia.ListaJuegos = CargarJuegos();
                agencia.ListaAssemblies = CargarDll();
                agencia.ListaEntidades = CargarAssemblies();
                agencia.ManejadorApuestas = new ManejadorApuestas();
                agencia.UsuarioLogueado = new Usuarios();              
            }
            return agencia;
        }

        /// <summary>
        /// Busca en la Lista de Entidades del juego la primera ocurrencia de la entidad para buscar en la lista de Juegos (EntityFmw) el Juego asociado.
        /// </summary>
        /// <param name="iJuego"></param>
        /// <returns></returns>
        public Juegos GetJuegoAsociado(IJuegos iJuego) 
        {
            IJuegos IJuego = ListaEntidades.FirstOrDefault(o => o.GetType().Equals(iJuego.GetType()));
            int Index = ListaEntidades.IndexOf(IJuego);
            Juegos Juego = ListaJuegos.ElementAt(Index);

            return Juego;
        }

        private static List<IJuegos> CargarAssemblies()
        {
            IJuegos IJuego = null;
            List<IJuegos> Lista = new List<IJuegos>(); ;
            foreach (Assembly Assembly in agencia.ListaAssemblies)
            {
                foreach (Type i in Assembly.GetTypes())
                {
                    if (typeof(IJuegos).IsAssignableFrom(i))
                    {
                        IJuego = (IJuegos)Activator.CreateInstance(i);
                        Lista.Add(IJuego);
                    }
                }
            }
            return Lista;
        }

        public Juegos CargarAssembly(string dir)
        {
            string rutaAssembly = dir;
            Juegos juego = new Juegos();
            Juegos retorno = null;
            try
            {
                Assembly a = Assembly.LoadFile(rutaAssembly);
                bool carga = false;
                string NombreJuego = null;
                foreach (Type i in a.GetTypes())
                {
                    if (typeof(IJuegos).IsAssignableFrom(i))
                    {
                        juego.Descripcion = i.Name;
                        NombreJuego = i.Name;
                        juego.RutaAssembly = dir;
                        juego.Activo = true;
                        //compruebo si ya no existe en la BD
                        if (!(juego.Exists()))
                        {
                            carga = true;
                            juego.GuardarJuego();
                        }
                        else
                        {
                            throw new ExistingJuegoException();
                        }
                    }
                }
                if (!carga)
                {
                    throw new InvalidJuegoException();
                }
                else
                {
                    retorno = juego;
                    Contexto.GetContexto().SaveChanges();
                } 
            }
            catch (BadImageFormatException ex) 
            {
                throw ex;
            }
            this.Notify();
            return retorno;
        }

        /// <summary>
        /// Refresca las entidades para la carga de los juegos.
        /// </summary>
        public void RefrescarEntidades() 
        {
            ListaJuegos = CargarJuegos();
            ListaAssemblies = CargarDll();
            ListaEntidades = CargarAssemblies();
        }

        /// <summary>
        /// Recarga un nuevo IJuego del tipo corriente al completar el formulario y agregarlo a la lista de apuestas.
        /// </summary>
        /// <param name="iJuego"></param>
        public static void CargarNuegoIJuego(IJuegos iJuego) 
        {
            IJuegos IJuego = (IJuegos)Activator.CreateInstance(iJuego.GetType());
            agencia.ListaEntidades.Add(IJuego);
        }

        /// <summary>
        /// Carga los juegos dados de alta en la base de datos
        /// </summary>
        /// <returns></returns>
        private static List<Juegos> CargarJuegos()
        {
            List<Juegos> ListaJuegos = Contexto.GetContexto().Juegos.Where(o => o.Activo == true).ToList();

            return ListaJuegos;
        }

        public void ResetListaEntidades() 
        {
            this.ListaEntidades = CargarAssemblies();
        }

        private static List<Assembly> CargarDll()
        {
            //Obtengo la ruta donde van a estar los assemblys (dll) desde el archivo app.config
            AgenciaEntities contexto = Contexto.GetContexto();
            List<string> ListaRutaAssembly = contexto.Juegos.Select(o => o.RutaAssembly).ToList();
            //List<Juegos> ListaJuegos = CargarJuegos();
            List<Assembly> ListaAssemblies = new List<Assembly>();
            Assembly Assembly;
            foreach (var dir in ListaRutaAssembly)
            {
                Assembly = Assembly.LoadFile(dir);  //FIXME:
                ListaAssemblies.Add(Assembly);
            }
            return ListaAssemblies;
        }

        /// <summary>
        /// Realiza la apuesta al WS.
        /// </summary>
        /// <param name="iJuego"></param>
        /// <returns></returns>
        public DatosApuesta RealizarApuesta(IJuegos iJuego)
        {
            //Devuelve los datos de la apuesta, número y monto apostado
            DatosApuesta DatosApuesta = iJuego.RealizarApuesta();
            //GuardarApuesta(retorno);
            return DatosApuesta;
        }

        /// <summary>
        /// Verifica si la apuesta tiene premio.
        /// </summary>
        /// <param name="iJuego"></param>
        /// <param name="idApuesta"></param>
        /// <returns></returns>
        public double CorrespondePremio(IJuegos iJuego, string idApuesta)
        {
            Guid guid;

            if (!(idApuesta.Trim().Equals("")))
            {
                try
                {
                    guid = new Guid(idApuesta);
                }
                catch (Exception)
                {
                    throw new IncorrectFormatTypeException();
                }
            }
            else
            {
                throw new EmptyPagosIdException();
            }

            return iJuego.CorrespondePremio(guid);
        }

        /// <summary>
        /// Pregunta al WS si el juego merece pago. De ser asi persiste la información del pago.
        /// </summary>
        /// <param name="iJuego"></param>
        /// <param name="idApuesta"></param>
        /// <param name="premio"></param>
        /// <returns></returns>
        public bool EfectuarPago(IJuegos iJuego, string idApuesta, int premio)
        {
            Guid guid = new Guid(idApuesta);
            bool pago = iJuego.EfectuarPago(guid);
            if (pago)
            {
                AgenciaEntities contexto = Contexto.GetContexto();
                int Cantidad = contexto.Apuestas.Where(o => o.Identificador == idApuesta).Count();
                if (Cantidad == 1)
                {
                    Apuestas a = contexto.Apuestas.Where(o => o.Identificador == idApuesta).First();
                    a.Pagado = true;
                    a.Premio = premio;
                    contexto.SaveChanges();
                    pago = true;
                }
            }
            return pago;
        }

        /// <summary>
        /// Con el objeto DatosApuesta y la entidad Juego correspondiente guarda la apuesta en la BD
        /// </summary>
        /// <param name="datosApuesta"></param>
        /// <param name="juego"></param>
        public void GuardarApuesta(DatosApuesta datosApuesta, Juegos juego)
        {
            Apuestas Apuesta = new Apuestas();
            Apuesta.Identificador = datosApuesta.id_apuesta;
            Apuesta.MontoApostado = datosApuesta.monto;
            Apuesta.ValoresApuesta = datosApuesta.valores;
            Apuesta.Usuario = UsuarioLogueado.IdUsuario;
            Apuesta.Pagado = false;
            Apuesta.MontoComision = juego.Comision;
            Apuesta.Premio = 0;
            Apuesta.Juego = juego.IdJuego;
            Apuesta.Fecha = System.DateTime.Now;

            Apuesta.GuardarApuesta();
            ManejadorLog.GetLog().Grabar("Guardar Apuesta: ");
        }

        /// <summary>
        /// Consultas al WebService
        /// </summary>
        /// <param name="juego"></param>
        /// <returns></returns>
        #region ConsultasWS 
        public List<float> RecaudacionJuego(Juegos juego)
        {
            List<float> Retorno = new List<float>();
            float SumaMontos = 0;
            float MontoTotal = 0;
            float Comision = 0;

            try
            {
                var MontosApostados = from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a.MontoApostado;
                List<int> listaMontosApostados = MontosApostados.ToList();
                SumaMontos = listaMontosApostados.Sum();
            }
            catch (InvalidOperationException ex)
            {
                ManejadorLog.GetLog().Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + this.UsuarioLogueado);
            }
            try
            {
                var Premios = from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a.Premio;
                List<int> listaPremios = Premios.ToList();
                MontoTotal = listaPremios.Sum();
            }
            catch (InvalidOperationException ex)
            {
                ManejadorLog.GetLog().Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + this.UsuarioLogueado);
            }
            try
            {
                float Ganancias = 0;
                List<Apuestas> listaApuestas = (from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a).ToList();
                foreach (Apuestas a in listaApuestas)
                {
                    float montoaux = a.MontoComision / (float)100;
                    Ganancias = Ganancias + (a.MontoApostado * montoaux);
                }
                Comision = Ganancias;
            }
            catch (InvalidOperationException ex)
            {
                ManejadorLog.GetLog().Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + this.UsuarioLogueado);
            }
            Retorno.Add(SumaMontos);
            Retorno.Add(MontoTotal);
            Retorno.Add(Comision);

            return Retorno;
        }

        public float GananciasAgencia(DateTime inicio, DateTime fechaFin)
        {
            float Ganancias = 0;
            var aux = from a in Contexto.GetContexto().Apuestas where (a.Fecha >= inicio && a.Fecha <= fechaFin) select a;
            List<Apuestas> ListaApuestas = aux.ToList();
            if (ListaApuestas.Count > 0)
            {
                foreach (Apuestas Apuesta in ListaApuestas)
                {
                    float Montoaux = Apuesta.MontoComision / (float)100;
                    Ganancias = Ganancias + (Apuesta.MontoApostado * Montoaux);
                }
            }
            else 
            { 
                throw new NoApuestasException();
            }

            return Ganancias;
        }

        public bool ApuestaYaPagada(IJuegos iJuego, string idApuesta)
        {
            //AgenciaEntities contexto = Contexto.GetContexto();
            //Apuestas Apuesta = contexto.Apuestas.Where(o => o.Identificador == idApuesta).First();
            Guid guid = new Guid(idApuesta);
            return(!(iJuego.EfectuarPago(guid)));
            //return (bool)Apuesta.Pagado;
        }
        #endregion
    }
}
